/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License.You may obtain a copy of the License at
	http://www.mozilla.org/MPL/MPL-1.1.html
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import org.aswing.Component;
import org.aswing.JPanel;

import com.bourre.data.collections.Map;

import fever.exception.IllegalArgumentException;

import flash.filters.BevelFilter;
import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.filters.GradientBevelFilter;
import flash.filters.GradientGlowFilter;

/**
 * Apply <strong>Flash filter</strong> on <strong>AsWing components</strong>.
 * 
 * <p>All methods starting with <strong>'set'</strong> ( ie {@link #setShadow} ) 
 * don't enqueue filter effects.<br />
 * If you call {@link #setShadow} twice, only one shadow filter is applied.
 * 
 * <p>If you want to enqueue filters, use methods starting with <strong>'add'</strong>
 * 
 * <p>Example
 * {@code
 *   public function run() : Void 
 *   {
 *   	// 1st way : create filter component and define filters
 *   	var button : JButton = new JButton( "click me" );
 *   	button.addActionListener( _removeFilter, this );
 *   	
 *   	var filter : FvComponentFilter = new FvComponentFilter( button );
 *   	filter.setShadow();
 *   	filter.addBlur();
 *   	
 *   	// 2nd way : create and apply filter in one step
 *   	var button2 : JButton = new JButton( "click me" );
 *   	button2.addActionListener( _removeFilter2, this );
 *   	
 *   	FvComponentFilter.create( button2, new GlowFilter() );
 *   	
 *   	// retreives FvAsWing main container
 *   	var pane : Container = FvAsWing.getInstance().getContainer();
 *   	pane.append( button, BorderLayout.NORTH );
 *   	pane.append( button2, BorderLayout.SOUTH );
 *   }
 *   
 *   private function _removeFilter( source : JButton ) : Void
 *   { 
 *   	var f : FvComponentFilter = FvComponentFilter.geComponentFilter( source );
 *   	f.clearFilterType( BlurFilter ); // clears filter by type
 *   	f.clearShadow(); // clear only shadow filter
 *   }	
 *  
 *   private function _removeFilter2( source : JButton ) : Void
 *   { 
 *   	var f : FvComponentFilter = FvComponentFilter.geComponentFilter( source );
 *   	f.clean();	// clear all filters
 *   }
 * }
 * 
 * @author Romain Ecarnot
 */
class fvaswing.graphics.FvComponentFilter
{
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private static var _a : Map = new Map();
	
	private var _mc : MovieClip;
	private var _buffer : Array;
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Creates a new {@link FvComponentFilter} instance and apply passed-in 
	 * {@code filter} for initialization. 
	 */
	public static function create( source : Component, filter : BitmapFilter ) : FvComponentFilter
	{
		try
		{
			var o : FvComponentFilter = new FvComponentFilter( source );
			if( filter ) o.addFilter( filter );
		}
		catch( e : IllegalArgumentException )
		{
			return null;
		}	
	}
	
	/**
	 * Returns {@link FvComponentFilter} where passed-in {@code source} is under 
	 * control.
	 */
	public static function geComponentFilter( source : Component ) : FvComponentFilter
	{
		return _a.get( source.getID() );
	}
	
	/**
	 * Constructor.
	 * 
	 * <p>Check if passed-in component is already created.<br />
	 * If not, we register listener to <strong>Component.ON_CREATED</strong> 
	 * event to retreive correct filter target ( MovieClip ).
	 * 
	 * <p>All filters applied before component is created are stored in a buffer.<br />
	 * They are applied when component is ready.
	 * 
	 * @param target Component target
	 * @throws {@link fever.exception.IllegalArgumentException} if target is 
	 * {@code undefined} or {@code null}
	 * 
	 * @throws {@link fever.exception.IllegalArgumentException} if target is 
	 * already under filter control.
	 */
	public function FvComponentFilter( target : Component )
	{
		if( !target )
			throw new IllegalArgumentException( 'Invalid filter target' );
		else if ( _a.containsKey( target.getID() ) )
			throw new IllegalArgumentException( 'Component is already under filter control with ID == ' + target.getID() );
		else
		{
			_buffer = new Array();
			_a.put( target.getID(), this );
			
			if( !target.isDisplayable() )
			{
				target.addEventListener( Component.ON_CREATED, _onCreated, this );	
			}
			else _createMovieClipTarget( target );
		}
	}
	
	/**
	 * Sets a shadow filter.
	 * 
	 * @param filter ( optional ) DropShadowFilter filter
	 */
	public function setShadow( filter : DropShadowFilter ) : DropShadowFilter
	{
		clearFilterType( DropShadowFilter );
		return addShadow( filter );
	}
	
	/**
	 * Adds a new shadow filter.
	 * 
	 * @param filter ( optional ) DropShadowFilter filter
	 */
	public function addShadow( filter : DropShadowFilter ) : DropShadowFilter
	{
		if( !filter ) filter = new DropShadowFilter();
		addFilter( BitmapFilter( filter ) );
        return filter;
	}
	
	/**
	 * Removes all shadow filter from current component.
	 */
	public function clearShadow() : Void
	{
		clearFilterType( DropShadowFilter );
	}
	
	/**
	 * Sets a blur filter.
	 * 
	 * @param filter ( optional ) BlurFilter filter
	 */
	public function setBlur( filter : BlurFilter ) : BlurFilter
	{
		clearFilterType( BlurFilter );
		return addBlur( filter );
	}
	
	/**
	 * Adds a new blur filter.
	 * 
	 * @param filter ( optional ) BlurFilter filter
	 */
	public function addBlur( filter : BlurFilter ) : BlurFilter
	{
		if( !filter ) filter = new BlurFilter();
		addFilter( BitmapFilter( filter ) );
        return filter;
	}
	
	/**
	 * Removes all blur filter from current component.
	 */
	public function clearBlur(  Component ) : Void
	{
		clearFilterType( BlurFilter );
	}
	
	/**
	 * Sets a glow filter.
	 * 
	 * @param filter ( optional ) GlowFilter filter
	 */
	public function setGlow( filter : GlowFilter ) : GlowFilter
	{
		clearFilterType( GlowFilter );
		return addGlow( filter );
	}
	
	/**
	 * Adds a new glow filter.
	 * 
	 * @param filter ( optional ) GlowFilter filter
	 */
	public function addGlow( filter : GlowFilter ) : GlowFilter
	{
		if( !filter ) filter = new GlowFilter();
		addFilter( BitmapFilter( filter ) );
        return filter;
	}
	
	/**
	 * Removes all glow filter from current component.
	 */
	public function clearGlow() : Void
	{
		clearFilterType( GlowFilter );
	}
	
	/**
	 * Sets a bevel filter.
	 * 
	 * @param filter ( optional ) BevelFilter filter
	 */
	public function setBevel( filter : BevelFilter ) : BevelFilter
	{
		clearFilterType( BevelFilter );
		return addBevel( filter );
	}
	
	/**
	 * Adds a new bevel filter.
	 * 
	 * @param filter ( optional ) BevelFilter filter
	 */
	public function addBevel( filter : BevelFilter ) : BevelFilter
	{
		if( !filter ) filter = new BevelFilter();
		addFilter( BitmapFilter( filter ) );
		return filter;
	}
	
	/**
	 * Removes all bevel filter from current component.
	 */
	public function clearBevel() : Void
	{
		clearFilterType( BevelFilter );
	}
	
	/**
	 * Sets a color filter.
	 * 
	 * @param filter ColorMatrixFilter filter
	 */
	public function setColorMatrixMixer( filter : ColorMatrixFilter ) : ColorMatrixFilter
	{
		if( !filter ) return null;
		clearFilterType( ColorMatrixFilter );
		return addColorMatrixMixer( filter );
	}
	
	/**
	 * Adds a new color filter.
	 * 
	 * @param filter ColorMatrixFilter filter
	 */
	public function addColorMatrixMixer( filter : ColorMatrixFilter ) : ColorMatrixFilter
	{
		if( !filter ) return null;
		addFilter( BitmapFilter( filter ) );
		return filter;
	}
	
	/**
	 * Sets a gradient glow filter.
	 * 
	 * @param filter GradientGlowFilter filter
	 */
	public function setGradientGlow( filter : GradientGlowFilter ) : GradientGlowFilter
	{
		if( !filter ) return null;
		clearFilterType( GradientGlowFilter );
		return addGradientGlow( filter );
	}
	
	/**
	 * Adds a new gradient glow filter.
	 * 
	 * @param filter GradientGlowFilter filter
	 */
	public function addGradientGlow( filter : GradientGlowFilter ) : GradientGlowFilter
	{
		if( !filter ) filter = new GradientGlowFilter();
		addFilter( BitmapFilter( filter ) );
		return filter;
	}
	
	/**
	 * Removes all gradient glow filter from current component.
	 */
	public function clearGradientGlow() : Void
	{
		clearFilterType( GradientGlowFilter );
	}
	
	/**
	 * Sets a gradient bevel filter.
	 * 
	 * @param filter GradientGlowFilter filter
	 */
	public function setGradientBevel( filter : GradientBevelFilter ) : GradientBevelFilter
	{
		if( !filter ) return null;
		clearFilterType( GradientBevelFilter );
		return addGradientBevel( filter );
	}
	
	/**
	 * Adds a new gradient bevel filter.
	 * 
	 * @param filter GradientBevelFilter filter
	 */
	public function addGradientBevel( filter : GradientBevelFilter ) : GradientBevelFilter
	{
		if( !filter ) filter = new GradientBevelFilter();
		addFilter( BitmapFilter( filter ) );
        return filter;
	}
	
	/**
	 * Removes all gradient bevel filter from current component.
	 */
	public function clearGradientBevel() : Void
	{
		clearFilterType( GradientBevelFilter );
	}
	
	/**
	 * Adds passed-in filter to component.
	 * 
	 * @param filter BitmapFilter instance
	 */
	public function addFilter(  filter : BitmapFilter ) : Void
	{
		if( !_mc ) 
		{
			_buffer.push( filter );
			return;
		}
		
		var tmp : Array = _mc.filters;
		tmp.push( filter );
		_mc.filters = tmp;
	}
	
	/**
	 * Removes passed-in filter from current component.
	 * 
	 * @param filter BitmapFilter instance
	 */
	public function removeFilter( filter : BitmapFilter ) : Void
	{
		if( !filter ) return;
		
        var tmp : Array = _mc.filters;
        var index : Number  = getFilterIndex( filter );
        if ( index > -1 )
        {
            tmp.splice( index, 1 );             
            _mc.filters = tmp;
        }
	}
	
	/**
	 * Removes all filters from component.
	 */
	public function clean() : Void
	{
		_mc.filters = new Array();
		_buffer = new Array();
	}
	
	/**
	 * Removes all filter ( type as {@code filterType} parameter ) from 
	 * current component.
	 * 
	 * @param filterType Filter type
	 */
	public function clearFilterType( filterType : Function ) : Void
	{
		if( !filterType ) return;
		
		var len : Number = _mc.filters.length;
		var tmp : Array = new Array();
		
		for( var i : Number = 0; i < len; i++ )
		{
			var f : BitmapFilter = _mc.filters[i];
			if( !( f instanceof filterType ) )
			{
				tmp.push( f );
			}
		}
		_mc.filters = tmp;
	}
	
	/**
	 * Returns filter index ( Filter Array position ).<br />
	 * The search is made on filter instance.
	 * 
	 * <p>Returns -1 if passed-in filter is not applied on component.
	 * 
	 * @param filterType Filter to search for in component
	 */
	public function getFilterIndex( filterType : BitmapFilter ) : Number
	{
        var pos : Number = _mc.filters.length;
        while( pos-- )
        {
            var f : BitmapFilter = _mc.filters[pos];
            if( f == filterType )
            {
               return pos;
            }
        }
        return -1;
	}
	
	/**
	 * Returns filter index ( Filter Array position ).<br />
	 * The search is made on filter type.
	 * 
	 * <p>Returns -1 if passed-in filter type is not applied on component.
	 * 
	 * @param filterType Filter to search for in component
	 */
	public function getFilterTypeIndex( filterType : Function ) : Number
	{
        var pos : Number = _mc.filters.length;
        while( pos-- )
        {
            var f : BitmapFilter = _mc.filters[pos];
            if( f instanceof filterType ) 
            {
               return pos;
            }
        }
        return -1;
	}
	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
	
	/**
	 * Triggered when component is created.<br />
	 * Thus we can retreive our filter target.
	 * 
	 * @param source Component
	 */
	private function _createMovieClipTarget( source : Component ) : Void
	{
		var tmp : MovieClip = source.createMovieClipOnRoot( "__ft__" );
		_mc = tmp._parent;
		tmp.removeMovieClip();	
	}
	
	private function _onCreated( source : JPanel ) : Void
	{
		_createMovieClipTarget( source );
		_emptyBuffer();
	}
	
	private function _emptyBuffer() : Void
	{
		var l : Number = _buffer.length;
		for (var i : Number = 0; i < l; i++ ) addFilter( _buffer[i] );
		_buffer = new Array();
	}
}